package com.lzy.mall.order.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import com.lzy.mall.common.constant.MQConstant;
import com.lzy.mall.common.constant.OrderConstant;
import com.lzy.mall.common.enume.OrderStatusEnum;
import com.lzy.mall.common.exception.CommonError;
import com.lzy.mall.common.to.OrderTo;
import com.lzy.mall.common.to.mq.SeckillOrderTo;
import com.lzy.mall.common.utils.PageUtils;
import com.lzy.mall.common.utils.ResultUtils;
import com.lzy.mall.common.vo.MemberRespVo;
import com.lzy.mall.common.vo.SkuStockVO;
import com.lzy.mall.order.entity.OmsOrder;
import com.lzy.mall.order.dao.OmsOrderDao;
import com.lzy.mall.order.entity.OmsOrderItem;
import com.lzy.mall.order.entity.OmsPaymentInfo;
import com.lzy.mall.order.feign.CartFeignService;
import com.lzy.mall.order.feign.MemberFeignService;
import com.lzy.mall.order.feign.ProductFeignService;
import com.lzy.mall.order.feign.WmsFeignService;
import com.lzy.mall.order.interceptor.LoginUserInterceptor;
import com.lzy.mall.order.service.OmsOrderItemService;
import com.lzy.mall.order.service.OmsOrderService;
import com.lzy.mall.order.service.OmsPaymentInfoService;
import com.lzy.mall.order.to.OrderCreateTo;
import com.lzy.mall.order.vo.*;
import com.sun.org.apache.regexp.internal.RE;
import io.renren.common.exception.RRException;
import lombok.extern.slf4j.Slf4j;
import org.beetl.sql.core.engine.PageQuery;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


/**
 * 订单(OmsOrder)表服务实现类
 *
 * @author lzy
 * @since 2021-08-25 11:22:37
 */
@Service
@RequiredArgsConstructor(onConstructor_ = {@Autowired})
@Slf4j
public class OmsOrderServiceImpl implements OmsOrderService {
    private final OmsOrderDao omsOrderDao;

    private final OmsOrderItemService omsOrderItemService;

    private final MemberFeignService memberFeignService;

    private final CartFeignService cartFeignService;

    private final WmsFeignService wmsFeignService;

    private final ProductFeignService productFeignService;

    private final ThreadPoolExecutor executor;

    private final StringRedisTemplate redisTemplate;

    private final RabbitTemplate rabbitTemplate;

    private final OmsPaymentInfoService paymentInfoService;

    @Override
    public OrderConfirmVo confirmOrder() {
        OrderConfirmVo orderConfirmVo = new OrderConfirmVo();
        MemberRespVo memberRespVo = LoginUserInterceptor.loginUser.get();

        //获取之前的请求
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();

        CompletableFuture<Void> addressFuture = CompletableFuture.runAsync(() -> {
            //每一个线程共享之前请求的数据
            RequestContextHolder.setRequestAttributes(requestAttributes);
            //1.远程查询所有的收货地址列表
            List<MemberAddressVo> address = memberFeignService.getAddress(memberRespVo.getId());
            orderConfirmVo.setAddress(address);
            log.info("远程查询所有的收货地址列表:{}", address);
        }, executor);

        CompletableFuture<Void> itemsFuture = CompletableFuture.runAsync(() -> {
            //每一个线程共享之前请求的数据
            RequestContextHolder.setRequestAttributes(requestAttributes);
            //2.远程查询购物车所有选中的购物项
            List<OrderItemVo> items = cartFeignService.getCurrentUserCartItem();
            orderConfirmVo.setItems(items);
            log.info("远程查询购物车所有选中的购物项:{}", items);
        }, executor).thenRunAsync(() -> {
            List<OrderItemVo> items = orderConfirmVo.getItems();
            List<Long> collect = items.stream().map(OrderItemVo::getSkuId).collect(Collectors.toList());
            log.info("skuId:{}", collect);
            List<SkuStockVO> list = null;
            try {
                list = wmsFeignService.getSkuStock(collect);
                log.info("库存远程调用：{}", list);
            } catch (Exception exception) {
                log.info("库存远程调用有异常:{}",exception.getMessage());
            }
            if (CollUtil.isNotEmpty(list)) {
                Map<Long, Boolean> map = list.stream().collect(Collectors.toMap(SkuStockVO::getSkuId, item -> item.getStock() > 0));
                orderConfirmVo.setStocks(map);
            }
        }, executor);


        //3.查询用户积分
        Integer integration = memberRespVo.getIntegration();
        orderConfirmVo.setIntegration(integration);

        //4.其他数据自动计算

        //5.TODO 5.防重令牌
        String token = UUID.randomUUID().toString().replace("-", "");
        orderConfirmVo.setOrderToken(token);
        redisTemplate.opsForValue().set(OrderConstant.USER_ORDER_TOKEN_PREFIX + memberRespVo.getId(), token, 10, TimeUnit.MINUTES);

        try {
            CompletableFuture.allOf(addressFuture, itemsFuture).get();
        } catch (Exception e) {
            e.printStackTrace();
        }
        log.info("订单结算结果：{}", orderConfirmVo);
        return orderConfirmVo;
    }

    @Transactional
    @Override
    public SubmitOrderResponseVo submitOrder(OrderSubmitVo vo) {
        MemberRespVo memberRespVo = LoginUserInterceptor.loginUser.get();

        SubmitOrderResponseVo submitVo = new SubmitOrderResponseVo();
        submitVo.setCode(0);
        //下单：去创建订单，验令牌，验价格，锁库存。。。
        //1.验证令牌【令牌的对比和删除必须保证原子性】
        String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
        String token = vo.getOrderToken();
        // 0 令牌删除失败 1删除成功
        Long result = redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Arrays.asList(OrderConstant.USER_ORDER_TOKEN_PREFIX + memberRespVo.getId()), token);
        if (result == 0L) {
            // 令牌验证失败
            submitVo.setCode(1);
        } else {
            // 令牌验证成功
            log.info("下单参数：{}",vo);
            // 1 .创建订单等信息
            OrderCreateTo order = createOrder(vo);
            //2.验价
            BigDecimal payAmount = order.getOrder().getPayAmount();
            BigDecimal voPayPrice = vo.getPayPrice();
            if(Math.abs(payAmount.subtract(voPayPrice).doubleValue()) < 0.01){
                //金额对比
                //3.保存订单
                saveOrder(order);

                //4.库存锁定,异常回滚
                WareSkuLockVo lockVo = new WareSkuLockVo();
                lockVo.setOrderSn(order.getOrder().getOrderSn());
                List<OrderItemVo> locks = order.getOrderItems().stream().map(item -> {
                    OrderItemVo itemVo = new OrderItemVo();
                    // 锁定的skuId 这个skuId要锁定的数量
                    itemVo.setSkuId(item.getSkuId());
                    itemVo.setCount(item.getSkuQuantity());
                    itemVo.setTitle(item.getSkuName());
                    return itemVo;
                }).collect(Collectors.toList());

                lockVo.setLocks(locks);
                // 远程锁库存
                ResultUtils r = wmsFeignService.orderLockStock(lockVo);
                log.info("远程锁库存:{}",r);
                if(r.getCode() == 0){
                    // 库存足够 锁定成功 给MQ发送消息
                    submitVo.setOrder(order.getOrder());
                    //TODO 订单创建成功给MQ发送消息
                    rabbitTemplate.convertAndSend(MQConstant.orderEventExchange,MQConstant.orderCreateRK,order.getOrder());
                }else{
                    // 锁定失败
                    String msg = (String) r.get("msg");
                    throw new RRException(CommonError.NOT_STOCK_EXCEPTION);
                }
            }else {
                // 价格验证失败
                submitVo.setCode(2);
            }
        }
        return submitVo;
    }

    @Override
    public OmsOrder getOrderBySn(String orderSn) {
        return omsOrderDao.createLambdaQuery()
                .andEq(OmsOrder::getOrderSn,orderSn)
                .single();
    }

    @Override
    public void closeOrder(OmsOrder order) {
        log.info("关闭订单-订单：{}",order);
        if (OrderStatusEnum.CREATE_NEW.getCode().equals(order.getStatus())){
            order.setStatus(OrderStatusEnum.CANCLED.getCode());
            omsOrderDao.updateTemplateById(order);
            OrderTo orderTo = new OrderTo();
            BeanUtil.copyProperties(order,orderTo);
            rabbitTemplate.convertAndSend(MQConstant.orderEventExchange,MQConstant.orderReleaseOtherRK,orderTo);
        }
    }

    @Override
    public PayVo getOrderPay(String orderSn) {
        PayVo payVo = new PayVo();
        OmsOrder order = getOrderBySn(orderSn);
        payVo.setOut_trade_no(orderSn);
        payVo.setTotal_amount(order.getTotalAmount()
                        .add(order.getFreightAmount()==null?new BigDecimal("0"):order.getFreightAmount())
                        .setScale(2,BigDecimal.ROUND_UP)
                        .toString());
        payVo.setSubject("订单支付");
        payVo.setBody("订单支付");
        return payVo;
    }

    @Override
    public PageUtils queryPageWithItem(Long pageNum) {

        MemberRespVo respVo = LoginUserInterceptor.loginUser.get();

        PageQuery<OmsOrder> page = omsOrderDao.createLambdaQuery()
                .andEq(OmsOrder::getMemberId, respVo.getId())
                //.andNotEq(OmsOrder::getStatus,OrderStatusEnum.CANCLED.getCode())
                .desc(OmsOrder::getId)
                .page(pageNum, 10);

        PageQuery<OrderListWithItemVo> pageQuery = new PageQuery<>();
        pageQuery.setPageNumber(page.getPageNumber());
        pageQuery.setPageSize(page.getPageSize());
        pageQuery.setTotalRow(page.getTotalRow());
        pageQuery.setPageSize(page.getPageSize());


        List<OmsOrderItem> orderItems = omsOrderItemService.list();
        List<OmsOrder> list = page.getList();
        List<OrderListWithItemVo> collect = list.stream().map(order -> {
            OrderListWithItemVo itemVo = new OrderListWithItemVo();
            BeanUtil.copyProperties(order, itemVo);
            List<OmsOrderItem> list1 = orderItems.stream().filter(omsOrderItem ->
                    omsOrderItem.getOrderSn().equals(order.getOrderSn()))
                    .collect(Collectors.toList());
            itemVo.setItemEntities(list1);
            return itemVo;
        }).collect(Collectors.toList());


        pageQuery.setList(collect);

        return new PageUtils(pageQuery);
    }

    @Override
    public String handlePayResult(PayAsyncVo vo) {
        // 1.保存交易流水
        OmsPaymentInfo infoEntity = new OmsPaymentInfo();
        infoEntity.setAlipayTradeNo(vo.getTrade_no());
        infoEntity.setOrderSn(vo.getOut_trade_no());
        //		TRADE_SUCCESS
        infoEntity.setPaymentStatus(vo.getTrade_status());
        infoEntity.setCallbackTime(vo.getNotify_time());
        infoEntity.setSubject(vo.getSubject());
        infoEntity.setTotalAmount(new BigDecimal(vo.getTotal_amount()));
        infoEntity.setCreateTime(vo.getGmt_create());
        paymentInfoService.save(infoEntity);

        // 2.修改订单状态信息
        if(vo.getTrade_status().equals("TRADE_SUCCESS") || vo.getTrade_status().equals("TRADE_FINISHED")){
            // 支付成功
            String orderSn = vo.getOut_trade_no();
            OmsOrder single = omsOrderDao.createLambdaQuery()
                    .andEq(OmsOrder::getOrderSn, orderSn)
                    .single();
            single.setStatus(OrderStatusEnum.PAYED.getCode());
            omsOrderDao.updateTemplateById(single);
        }
        return "success";
    }

    @Override
    public void createSeckillOrder(SeckillOrderTo seckillOrderTo) {
        //TODO 保存订单信息
        OmsOrder order = new OmsOrder();

        order.setOrderSn(seckillOrderTo.getOrderSn());
        order.setMemberId(seckillOrderTo.getMemberId());
        order.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
        BigDecimal multiply = seckillOrderTo.getSeckillPrice().multiply(new BigDecimal("" + seckillOrderTo.getNum()));
        order.setPayAmount(multiply);

        order.setCreateTime(DateUtil.date());

        // 2. 获取收获地址信息
//        ResultUtils result = wmsFeignService.getFare();
//        FareVo resp = (FareVo) result.getBean(FareVo.class);
//        log.info("wms远程调用FareVo：{}",resp);
//        order.setMemberUsername(resp.getAddressVo().getName());
//        order.setFreightAmount(resp.getFare());
//        order.setReceiverCity(resp.getAddressVo().getCity());
//        order.setReceiverDetailAddress(resp.getAddressVo().getDetailAddress());
//        order.setDeleteStatus(OrderStatusEnum.CREATE_NEW.getCode());
//        order.setReceiverPhone(resp.getAddressVo().getPhone());
//        order.setReceiverName(resp.getAddressVo().getName());
//        order.setReceiverPostCode(resp.getAddressVo().getPostCode());
//        order.setReceiverProvince(resp.getAddressVo().getProvince());
//        order.setReceiverRegion(resp.getAddressVo().getRegion());
        // 设置订单状态
        order.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
        order.setAutoConfirmDay(7);

        omsOrderDao.insertTemplate(order);

        // TODO 保存订单项信息
        OmsOrderItem orderItem = new OmsOrderItem();
        orderItem.setOrderSn(seckillOrderTo.getOrderSn());
        orderItem.setRealAmount(multiply);
        orderItem.setSkuQuantity(seckillOrderTo.getNum());

        ResultUtils r = productFeignService.getSkuInfoBySkuId(seckillOrderTo.getSkuId());
        SpuInfoVo spuInfo = (SpuInfoVo) r.getBean(SpuInfoVo.class);
        log.info("远程调用商品product--SpuInfoVo：{}",spuInfo);
        orderItem.setSpuId(spuInfo.getId());
        orderItem.setSpuBrand(spuInfo.getBrandId().toString());
        orderItem.setSpuName(spuInfo.getSpuName());
        orderItem.setCategoryId(spuInfo.getCatalogId());
        //TODO 获取当前sku详细信息进行设置 productFeignService.getSpuInfoBySkuId()

        omsOrderItemService.save(orderItem);
    }

    /**
     * 保存订单数据
     * @param order
     */
    private void saveOrder(OrderCreateTo order) {
        OmsOrder omsOrder = order.getOrder();
        omsOrder.setModifyTime(DateUtil.date());
        omsOrderDao.insertTemplate(omsOrder,true);
        log.info("保存订单成功");

        List<OmsOrderItem> orderItems = order.getOrderItems();
        omsOrderItemService.saveBatch(orderItems);
        log.info("保存item成功");
    }

    /**
     * 创建订单
     *
     * @param submitVo
     */
    private OrderCreateTo createOrder(OrderSubmitVo submitVo) {
        OrderCreateTo orderCreateTo = new OrderCreateTo();
        // 1. 生成一个订单号
        String orderSn = IdUtil.objectId();

        OmsOrder orderEntity = buildOrderSn(orderSn, submitVo);

        // 2. 获取所有订单项
        List<OmsOrderItem> items = buildOrderItems(orderSn);

        // 3.验价	传入订单 、订单项 计算价格、积分、成长值等相关信息
        computerPrice(orderEntity, items);
        
        orderCreateTo.setOrder(orderEntity);
        orderCreateTo.setOrderItems(items);
        log.info("创建订单成功：{}",orderSn);
        return orderCreateTo;
    }

    private void computerPrice(OmsOrder orderEntity, List<OmsOrderItem> items) {
        BigDecimal totalPrice = new BigDecimal("0.0");
        // 叠加每一个订单项的金额
        BigDecimal coupon = new BigDecimal("0.0");
        BigDecimal integration = new BigDecimal("0.0");
        BigDecimal promotion = new BigDecimal("0.0");
        BigDecimal gift = new BigDecimal("0.0");
        BigDecimal growth = new BigDecimal("0.0");
        for (OmsOrderItem item : items) {
            // 优惠券的金额
            coupon = coupon.add(item.getCouponAmount());
            // 积分优惠的金额
            integration = integration.add(item.getIntegrationAmount());
            // 打折的金额
            promotion = promotion.add(item.getPromotionAmount());
            BigDecimal realAmount = item.getRealAmount();
            totalPrice = totalPrice.add(realAmount);

            // 购物获取的积分、成长值
            gift.add(new BigDecimal(item.getGiftIntegration().toString()));
            growth.add(new BigDecimal(item.getGiftGrowth().toString()));
        }
        // 1.订单价格相关 总额、应付总额
        orderEntity.setTotalAmount(totalPrice);
        orderEntity.setPayAmount(totalPrice.add(orderEntity.getFreightAmount()));

        orderEntity.setPromotionAmount(promotion);
        orderEntity.setIntegrationAmount(integration);
        orderEntity.setCouponAmount(coupon);

        // 设置积分、成长值
        orderEntity.setIntegration(gift.intValue());
        orderEntity.setGrowth(growth.intValue());

        // 设置订单的删除状态
        orderEntity.setDeleteStatus(OrderStatusEnum.CREATE_NEW.getCode());
    }

    private List<OmsOrderItem> buildOrderItems(String orderSn) {
        // 这里是最后一次来确认购物项的价格 这个远程方法还会查询一次数据库
        List<OrderItemVo> cartItems = cartFeignService.getCurrentUserCartItem();
        List<OmsOrderItem> itemEntities = null;
        if (cartItems != null && cartItems.size() > 0) {
            itemEntities = cartItems.stream().map(cartItem -> {
                OmsOrderItem itemEntity = buildOrderItem(cartItem);
                itemEntity.setOrderSn(orderSn);
                return itemEntity;
            }).collect(Collectors.toList());
        }
        return itemEntities;
    }

    private OmsOrderItem buildOrderItem(OrderItemVo cartItem) {
        OmsOrderItem itemEntity = new OmsOrderItem();
        // 1.订单信息： 订单号

        // 2.商品spu信息
        Long skuId = cartItem.getSkuId();
        ResultUtils r = productFeignService.getSkuInfoBySkuId(skuId);
        SpuInfoVo spuInfo = (SpuInfoVo) r.getBean(SpuInfoVo.class);
        log.info("远程调用商品product--SpuInfoVo：{}",spuInfo);
        itemEntity.setSpuId(spuInfo.getId());
        itemEntity.setSpuBrand(spuInfo.getBrandId().toString());
        itemEntity.setSpuName(spuInfo.getSpuName());
        itemEntity.setCategoryId(spuInfo.getCatalogId());
        // 3.商品的sku信息
        itemEntity.setSkuId(cartItem.getSkuId());
        itemEntity.setSkuName(cartItem.getTitle());
        itemEntity.setSkuPic(cartItem.getImage());
        itemEntity.setSkuPrice(cartItem.getPrice());
        // 把一个集合按照指定的字符串进行分割得到一个字符串
        String skuAttr = StringUtils.collectionToDelimitedString(cartItem.getSkuAttr(), ";");
        itemEntity.setSkuAttrsVals(skuAttr);
        itemEntity.setSkuQuantity(cartItem.getCount());
        // 4.积分信息 买的数量越多积分越多 成长值越多
        itemEntity.setGiftGrowth(cartItem.getPrice().multiply(new BigDecimal(cartItem.getCount())).intValue());
        itemEntity.setGiftIntegration(cartItem.getPrice().multiply(new BigDecimal(cartItem.getCount())).intValue());
        // 5.订单项的价格信息 优惠金额
        itemEntity.setPromotionAmount(new BigDecimal("0.0"));
        itemEntity.setCouponAmount(new BigDecimal("0.0"));
        itemEntity.setIntegrationAmount(new BigDecimal("0.0"));
        // 当前订单项的实际金额
        BigDecimal orign = itemEntity.getSkuPrice().multiply(new BigDecimal(itemEntity.getSkuQuantity().toString()));
        // 减去各种优惠的价格
        BigDecimal subtract = orign.subtract(itemEntity.getCouponAmount()).subtract(itemEntity.getPromotionAmount()).subtract(itemEntity.getIntegrationAmount());
        itemEntity.setRealAmount(subtract);
        return itemEntity;
    }

    /**
     * 构建一个订单
     */
    private OmsOrder buildOrderSn(String orderSn, OrderSubmitVo submitVo) {
        OmsOrder entity = new OmsOrder();
        entity.setOrderSn(orderSn);
        entity.setCreateTime(new Date());
        MemberRespVo rsepVo = LoginUserInterceptor.loginUser.get();
        entity.setMemberId(rsepVo.getId());
        entity.setMemberUsername(rsepVo.getUsername());
        entity.setBillReceiverEmail(rsepVo.getEmail());
        // 2. 获取收获地址信息
        ResultUtils result = wmsFeignService.getFare(submitVo.getAddrId());
        FareVo resp = (FareVo) result.getBean(FareVo.class);
        log.info("wms远程调用FareVo：{}",resp);
        entity.setFreightAmount(resp.getFare());
        entity.setReceiverCity(resp.getAddressVo().getCity());
        entity.setReceiverDetailAddress(resp.getAddressVo().getDetailAddress());
        entity.setDeleteStatus(OrderStatusEnum.CREATE_NEW.getCode());
        entity.setReceiverPhone(resp.getAddressVo().getPhone());
        entity.setReceiverName(resp.getAddressVo().getName());
        entity.setReceiverPostCode(resp.getAddressVo().getPostCode());
        entity.setReceiverProvince(resp.getAddressVo().getProvince());
        entity.setReceiverRegion(resp.getAddressVo().getRegion());
        // 设置订单状态
        entity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
        entity.setAutoConfirmDay(7);
        return entity;
    }

}
